﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;

using Storm;
using Storm.Docking;
using Storm.Docking.Controls;
using Storm.Docking.Controls.Float;
using Storm.Docking.Visual;
using Storm.Docking.Visual.Drawing;

namespace Storm.Docking.Controls
{
    /// <summary>
    /// Represents a docked panel in a DockPane.
    /// </summary>
	[ToolboxItem(false)]
    public class DockPanel
        : Panel
    {
        #region Fields

        private DockPane     dockPane = null;
		private DockRenderer renderer = null;

        private DockTab   dockTab      = null;
        private DockTab   mainDockTab  = null;
        private TabPage   mainTabPage  = null;

        private DockCaption   dockCaption   = null;
        private DockContainer dockContainer = null;

        private DockForm       dockForm       = null;
        private DockPanelState dockPanelState = DockPanelState.Docked;

        private string captionTitle = "DockPanel";

        #endregion

        #region Properties

		/// <summary>
		/// Gets the DockTab of the DockPanel.
		/// </summary>
		[Browsable(false)]
		[Description("Gets the DockTab of the DockPanel.")]
		public DockTab DockTab
		{
			get { return dockTab; }
		}

        /// <summary>
        /// Gets the DockCaption of the DockPanel.
        /// </summary>
        [Browsable(false)]
        [Description("Gets the DockCaption of the DockPanel.")]
        public DockCaption DockCaption
        {
            get { return dockCaption; }
        }

		/// <summary>
		/// Gets the DockContainer child of the DockPanel.
		/// </summary>
		[Browsable(false)]
		[Description("Gets the DockContainer child of the DockPanel.")]
		public DockContainer DockContainer
		{
			get { return dockContainer; }
		}

        /// <summary>
        /// Gets or sets the parent DockPane of the DockPanel.
        /// </summary>
        [Browsable(false)]
        [Description("Gets or sets the parent DockPane of the DockPanel.")]
        public DockPane DockPane
        {
            get { return dockPane; }
            set
			{
				dockPane = value;
				this.DockTab.ImageList = dockPane.TabImageList;
			}
        }

        /// <summary>
        /// Gets or sets the DockPanel's caption title.
        /// </summary>
        [Browsable(false)]
        [Description("Gets or sets the DockPanel's caption title.")]
        public string CaptionTitle
        {
            get { return captionTitle; }
            set
            {
                captionTitle = value;
                this.Invalidate();  // Call Invalidate to draw.
            }
        }

		/// <summary>
		/// Gets the DockPanel's and its children's DockRenderer.
		/// </summary>
		[Browsable(false)]
		[Description("Gets the DockPanel's and its children's DockRenderer.")]
		public DockRenderer Renderer
		{
			get { return renderer; }
			set
			{
				renderer = value;

				// Loop through all the child Controls
				// and update them and their Renderers.

				foreach (Control control in this.Controls)
				{
					if (control is DockTab)
						(control as DockTab).Renderer = this.Renderer;
				}

				try
				{
					this.DockCaption.Renderer = this.Renderer;
					this.DockTab.Renderer = this.Renderer;
					this.DockForm.Renderer = this.Renderer;
				}
				catch { }

				this.Invalidate();
			}
		}

		/// <summary>
		/// Gets or sets the Form of the DockPanel.
		/// </summary>
		[Browsable(false)]
		[Description("Gets or sets the Form of the DockPanel.")]
		public Form Form
		{
			get { return this.DockContainer.Form; }
			set
            {
                this.DockContainer.Form = value;

                // Update the text of the DockCaption, the 
                // DockTab, and the main TabPage.

                this.CaptionTitle = this.DockContainer.Form.Text;
                this.DockCaption.CaptionText = this.CaptionTitle;

                this.MainTabPage.ToolTipText = this.CaptionTitle;
                this.MainTabPage.Text = this.CaptionTitle;

				this.DockPane.UpdateIcons();
            }
		}

        /// <summary>
        /// Gets the main TabPage of the DockPanel.
        /// </summary>
        [Browsable(false)]
        [Description("Gets the main TabPage of the DockPanel.")]
        public TabPage MainTabPage
        {
            get { return mainTabPage; }
        }

        /// <summary>
        /// Gets or sets the main DockTab of the DockPanel.
        /// This is set internally by the DockPane parent.
        /// </summary>
        [Browsable(false)]
        [Description("Gets or sets the main DockTab of the DockPanel. This is set internally by the DockPane parent.")]
        public DockTab MainDockTab
        {
            get { return mainDockTab; }
            set { mainDockTab = value; }
        }

        /// <summary>
        /// Gets the current DockPanelState of the DockPanel.
        /// </summary>
        [Browsable(false)]
        [Description("Gets the current DockPanelState of the DockPanel.")]
        public DockPanelState DockPanelState
        {
            get { return dockPanelState; }
			internal set { dockPanelState = value; }
        }

		#region Internal

		/// <summary>
		/// Gets or sets the DockForm of the DockPanel.
		/// </summary>
		internal DockForm DockForm
		{
			get { return dockForm; }
			set { dockForm = value; }
		}

		#endregion

		#endregion

		#region Methods

		#region Public

		/// <summary>
        /// Undocks the DockPanel to make it float in space and able to
        /// be dragged around the screen.
        /// </summary>
        public void MakeFloating()
        {
			this.DockPane.UpdateIcons();

			dockForm = new DockForm();
			dockForm.Renderer = this.Renderer;
			this.MainDockTab.DockPanel.Renderer = this.Renderer;

			DockPanel dockPanel = this;
			dockPanel.Dock = DockStyle.Fill;

			foreach (TabPage page in dockPanel.MainDockTab.TabPages)
			{
				foreach (Control control in page.Controls)
				{
					if (control is DockContainer)
					{
						DockContainer container = control as DockContainer;
						container.DockPanel.DockForm = dockForm;
					}
				}
			}

			dockForm.Location = Cursor.Position;
			dockForm.Controls.Add(dockPanel);
			dockForm.Renderer = this.Renderer;
			dockForm.Show();

			this.DockPanelState = DockPanelState.Floating;
			this.DockPane.OnFloat(this.DockPane, new DockEventArgs(this, DockStyle.None));
			this.DockPane.Controls.Remove(dockPanel);
        }

        /// <summary>
        /// Docks the DockPanel at the nearest dock side.
        /// </summary>
        public void MakeDocked()
        {
			// Store the current size so we can resize the DockPanel later. (BUG FIX)

			Size beforeSize = this.Size;

			DockStyle dockStyle = DockStyle.None;
			Point loc = Cursor.Position;

			if (this.DockPane.LeftRect.Bounds.Contains(loc) == true)
				dockStyle = DockStyle.Left;
			else if (this.DockPane.RightRect.Bounds.Contains(loc) == true)
				dockStyle = DockStyle.Right;
			else if (this.DockPane.TopRect.Bounds.Contains(loc) == true)
				dockStyle = DockStyle.Top;
			else if (this.DockPane.BottomRect.Bounds.Contains(loc) == true)
				dockStyle = DockStyle.Bottom;

			// If there's already a DockPanel that is docked to the found side, we have
			// to add a TabPage containing this DockPanel to its DockTab.

			DockPanel foundDockPanel = null;
			foreach (Control control in this.DockPane.Controls)
			{
				if (control is DockPanel)
				{
					DockPanel localDockPanel = control as DockPanel;
					if (localDockPanel.Dock == dockStyle)
					{
						foundDockPanel = localDockPanel;

						// We found a DockPanel - break.
						break;
					}
				}
			}

			if (foundDockPanel != null)
			{
				this.DockPane.OnDock(this.DockPane, new DockEventArgs(this, dockStyle));
				this.DockPanelState = DockPanelState.Docked;

				// BUG FIX: since there can be more than one TabPage in the DockPanel's DockTab,
				// we loop through its TabPages property and add them all to the found DockPanel.

				foreach (TabPage page in this.MainDockTab.TabPages)
				{
					foundDockPanel.MainDockTab.TabPages.Add(page);
					this.MainDockTab.DockPanel.Form = this.MainDockTab.DockPanel.Form;
				}

				foreach (TabPage page in this.DockTab.TabPages)
					foundDockPanel.DockTab.TabPages.Add(page);

				this.DockPane.Controls.Remove(this);

				// Update the Forms' Icons.

				this.DockPane.UpdateIcons();

				// Remember to hide the dock Rectangles.

				this.DockPane.HideDockRectangles();

				return;
			}

			if (dockStyle != DockStyle.None)
			{
				DockPanel dockPanel = this;

				if (dockPanel != null)
				{
					this.DockPane.OnDock(this.DockPane, new DockEventArgs(this, dockStyle));
					this.DockPanelState = DockPanelState.Docked;

					dockPanel.Dock = dockStyle;
					dockPanel.DockForm.Hide();

					if (dockPanel.MainDockTab.TabPages.Contains(dockPanel.MainTabPage) == false)
						dockPanel.MainDockTab.TabPages.Add(dockPanel.MainTabPage);

					dockPanel.MainTabPage.Controls.Add(dockPanel.DockContainer);
					dockPanel.Controls.Add(dockPanel.MainDockTab);
					dockPanel.Controls.Add(dockPanel.DockTab);
					dockPanel.Renderer = this.Renderer;
					this.DockPane.Controls.Add(dockPanel);

					// Then finally update our splitters.
					this.DockPane.PositionSplitters();

					// Update the size of the Form. (BUG FIX)
					// Somehow, the Form can sometimes be resized to fill the whole screen. 
					// We don't want that to happen.

					// HOWEVER, this bug fix only works if the DockStyle is Left or Right!
					// For Top and Bottom, we want to set the size of the DockPanel to match
					// the a maximum. We limit the maximum initial size of the DockPanel to 300.

					dockPanel.Size = beforeSize; // Do the bug fix for Left or Right.
					// Update: since I figured out the bug also was there for Top and Bottom,
					// I made the bug fix apply also to those.
				}
			}

			this.DockTab.SelectedIndex = this.DockTab.SelectedIndex;			// Reset to update shown Form.
			this.MainDockTab.SelectedIndex = this.MainDockTab.SelectedIndex;	// Reset to update shown Form.

			this.DockPane.HideDockRectangles();
        }

        /// <summary>
        /// Closes and hides the DockPanel.
        /// </summary>
        public void MakeClosed()
        {
			if (this.MainDockTab.TabPages.Contains(this.MainTabPage) == true)
			{
				this.MainDockTab.TabPages.Remove(this.MainTabPage);
				this.DockPanelState = DockPanelState.Closed;
			}
        }

		/// <summary>
		/// If the parent Form is a DockForm, updates its renderer.
		/// </summary>
		public void UpdateFormRenderer()
		{
			Form form = this.FindForm();
			if (form != null && form is DockForm)
				(form as DockForm).Renderer = this.Renderer;
		}

        #endregion

        #endregion

		/// <summary>
		/// Initializes a new instance of the <see cref="DockPanel"/> class.
		/// </summary>
        public DockPanel()
        {
			this.Renderer = new DockRenderer();

            // Setup the DockPanel for correct displaying
            // in its parent DockWindow.

            this.Dock = DockStyle.Right;
			this.BackColor = Renderer.DockColorTable.PanelBackColor;
			this.Font = Renderer.DockColorTable.PaneFont;

			// Then setup the DockTab for making the user
			// able to switch between visible windows.

			dockTab = new DockTab();

			this.DockTab.DockPanel = this;
            this.MainDockTab = this.DockTab;
			this.Controls.Add(this.DockTab);

			// Setup our DockCaption child to match our
			// needs in the DockContainer child.

			dockCaption = new DockCaption();

			this.DockCaption.CaptionText = captionTitle;
			this.DockCaption.DockPanel = this;

			// Now setup the DockContainer so we can add Forms and the
			// DockCaption to our DockTab Control.

			dockContainer = new DockContainer();

            this.DockContainer.Dock = DockStyle.Fill;
			this.DockContainer.DockPanel = this;
			this.DockContainer.DockCaption = this.DockCaption;

			this.DockContainer.Controls.Add(this.DockCaption);

			// Create a TabPage in the DockTab Control for the 
			// DockPanel and its children.

			TabPage dockPage = new TabPage();

            dockPage.ToolTipText = this.DockCaption.CaptionText;
			dockPage.Text = this.DockCaption.CaptionText;
			dockPage.Controls.Add(this.DockContainer);
			this.DockTab.TabPages.Add(dockPage);

            mainTabPage = dockPage;    // Don't lose reference.
        }
    }
}
